随笔分类
虚拟机栈
概述
由于跨平台的设计,java中指令都是根据栈来进行设计的,由于不同平台的cpu架构不同,所以不能设计为基于寄存器的
栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器
快速有效的分配内存方式
优点是跨平台,指令集小,编译器容易实现
缺点是性能下降,实现同样的功能需要更多的指令
Java虚拟机栈(Java Virtual Machine Stack):每个线程在创建时都会创建一个虚拟机栈,其内部保存着一个个的栈帧(Statck Frame,栈的基本单位),对应着一次次的Java方法调用(一个个栈帧的入栈和出栈)
栈管运行,堆管存储
前者解决程序的运行问题:程序怎么执行?怎么来处理数据?
后者解决数据的存储问题:数据怎么放?放在哪?
栈的局部变量表中放的是基本数据类型,如果是引用数据类型的话,在占中放的仅仅是这个对象的引用
不存在垃圾回收问题(仅涉及了栈帧的入栈和出栈操作)
作用:
主管Java程序的运行,它保存方法的局部变量(8种数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回
局部变量 VS 成员变量
基本数据类型 VS 引用类型变量(类、数组、接口)
栈中可能出现的异常
Java虚拟机规范允许Java栈的大小是动态的或者固定不变的
-
栈深度溢出:StackOverflowError
如果采用的是固定大小的Java虚拟机栈,那每一个线程的Java虚拟机栈的容量可以在线程创建的时候独立选定。如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机将会抛出一个StackOverflowError异常
不恰当的递归很可能会导致溢出异常的抛出
-
栈扩展失败:OutOfMemoryError
如果Java虚拟机可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在线程创建的时候没有足够的内存去创建对一个的虚拟机栈,Java虚拟机将会抛出一个OutOfMemoryError异常
设置栈内存大小
使用参数 -Xss 选项来设置线程的大小,栈的大小直接决定了函数调用的最大可达深度
-Xss size
Sets the thread stack size (in bytes). Append the letter k
or K
to indicate KB, m
or M
to indicate MB, and g
or G
to indicate GB. The default value depends on the platform:
- Linux/x64 (64-bit): 1024 KB
- macOS (64-bit): 1024 KB
- Oracle Solaris/x64 (64-bit): 1024 KB
- Windows: The default value depends on virtual memory
The following examples set the thread stack size to 1024 KB in different units:
-Xss1m
-Xss1024k
-Xss1048576
测试
/**
* 演示栈中的异常
* StackOverflowError
* 未设置大小之前 默认大小:与Virtual Machine相关 9893
* -xss250k :2284
*/
public class StackOverFlowTest {
public static int count = 0;
public static void main(String[] args) {
System.out.println(count++);
main(args);
}
}
存储基本单位
栈中的数据都是以栈帧(Stack Frame)的形式存储着的
在这个线程上执行的每个方法都各自对应着一个栈帧
栈帧是个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息
在一条活动线程中,一个时间点上,只会有一个活动着的栈帧
只有活动着的栈帧才是有效的,即当前栈帧(Current Method)
对应着得有:
当前方法 Current Method
当前类 Current Class
执行引擎运行的所有字节码指令只针对当前栈帧进行操作
不同线程中所包含的栈帧是不允许存在相互引用的,既不可能在一个栈帧中引用另一个线程的栈帧
即:线程私有的不可被共享,但是进程之间是可以实现内存数据的共享的
方法的结束方式分为两种:1.正常结束,以return为代表 2.方法执行中抛出异常(出现未捕获处理的异常)的方式结束
不管是哪种方式,都会导致栈帧的弹出
/**
* 栈帧
* 千层饼
*
* 方法的结束方式分为两种:1.正常结束,以return为代表 2.方法执行中出现未捕获处理的异常,以抛出异常的方式结束
* 不管是哪种方式,都会导致栈帧的弹出
*/
public class StackFrame {
public static void main(String[] args) {
StackFrame stackFrame = new StackFrame();
try {
stackFrame.methodA();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主线程正常结束了....");
}
public void methodA(){
System.out.println("methodA开始执行.........");
methodB();
System.out.println(5 / 0);
System.out.println("methodA执行结束........");
}
private void methodB() {
System.out.println("methodB开始执行.........");
methodC();
System.out.println("methodB执行结束........");
}
private void methodC() {
System.out.println("methodC开始执行.........");
System.out.println("methodC执行结束........");
}
}